#region License

// This file is part of Grawlix.
// 
// Grawlix is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
// Grawlix is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with Grawlix.  If not, see
// <http://www.gnu.org/licenses/>.

#endregion

using System;
using System.IO;
using Grawlix.Properties;

namespace Grawlix.Services.Resources
{
	/// <summary>
	/// The default implementation of an embedded resource
	/// resolver.
	/// </summary>
	public class EmbeddedResourceResolver : IUriResolver
	{
		/// <summary>
		/// The embedded resource URI scheme.
		/// </summary>
		private const string Embedded = "embedded";

		/// <summary>
		/// The base URI for field completion.
		/// </summary>
		private static readonly Uri _baseUri =
			new Uri(Embedded + "://");

		/// <summary>
		/// Initializes the <see cref="EmbeddedResourceResolver"/>
		/// class.
		/// </summary>
		static EmbeddedResourceResolver()
		{
			UriParser.Register(new FileStyleUriParser(), Embedded, -1);
		}

		/// <summary>
		/// Completes the given uri with the default scheme.
		/// </summary>
		/// <param name="uri">The URI to complete. Can be absolute
		/// (will have no effect).</param>
		/// <returns>A completed URI.</returns>
		public Uri CompleteUri(Uri uri)
		{
			if (uri.IsAbsoluteUri)
				return uri;
			uri = new Uri(_baseUri, uri);
			string embeddedPath = GetResourceName(uri);
			var resolved = new Uri(_baseUri, embeddedPath);
			return resolved;
		}

		/// <summary>
		/// Gets the common meat of a URI.
		/// </summary>
		/// <param name="uri">The URI to process.</param>
		/// <returns>The slightly formatted text.</returns>
		private static string GetResourceName(Uri uri)
		{
			return uri.AbsolutePath.Replace('/', '.').TrimStart('.');
		}

		/// <summary>
		/// Determines whether the specified scheme is supported.
		/// </summary>
		/// <param name="scheme">The URI scheme to test.</param>
		/// <returns>
		/// 	<c>true</c> if the specified scheme is supported;
		/// 	otherwise, <c>false</c>.
		/// </returns>
		public bool IsSupported(string scheme)
		{
			return scheme == Embedded;
		}

		/// <summary>
		/// Loads an embedded resource or Stream by its URI.
		/// </summary>
		/// <param name="uri">The URI that identifies the embedded
		/// resource.</param>
		/// <returns>The embedded resource or Stream found.</returns>
		/// <exception cref="IOException">A generic I/O error occurred.
		/// </exception>
		/// <exception cref="FileNotFoundException">The URI could not
		/// be resolved or does not point to an embedded resource.
		/// </exception>
		/// <remarks>This is a blocking operation which should only be
		/// called asynchronously.</remarks>
		/// <exception cref="ArgumentException">The URI scheme is not
		/// supported.</exception>
		public object Load(Uri uri)
		{
			object resource = TryLoad(uri);
			if (resource != null)
				return resource;
			throw new FileNotFoundException(
				"A request was made to load an embedded resource with " +
				"universal resource identifier (" + uri + "), " +
				"but no such resource was found. " +
				"The resource is not necessarily a critical component, " +
				"but its data will not be accessible.");
		}

		/// <summary>
		/// Tries to load an embedded resource or Stream by its URI.
		/// </summary>
		/// <param name="uri">The URI that identifies the embedded
		/// resource.</param>
		/// <returns>
		/// The embedded resource or Stream found, or null if
		/// none found.
		/// </returns>
		/// <exception cref="IOException">A generic I/O error occurred.
		/// </exception>
		/// <remarks>This is a blocking operation which should only be
		/// called asynchronously.</remarks>
		/// <exception cref="ArgumentException">The URI scheme is not
		/// supported.</exception>
		/// <exception cref="ArgumentNullException"><c>uri</c> is null.</exception>
		public object TryLoad(Uri uri)
		{
			if (uri == null)
				throw new ArgumentNullException("uri");
			if (!IsSupported(uri.Scheme))
			{
				throw new ArgumentException(
					"unsupported scheme " + uri.Scheme, "uri");
			}
			string embeddedPath = GetResourceName(uri);
			return BuiltIn.ResourceManager.GetObject(embeddedPath,
			                                         BuiltIn.Culture);
		}
	}
}